package com.corpgovernment.common.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import static java.util.Objects.requireNonNull;

/**
 * @author ：llw
 * @date ：Created in 2020/4/28 19:50
 * @description：
 * @modified By：
 * @version: $
 */
@Slf4j
public class CaffeineCacheUtil {

    /**
     * 分隔字符串
     */
    public static final String SPLITSTRING = "♀";
    /**
     * 所有缓存的存放处，根据Key和Value的类型决定cache
     */
    private final static ConcurrentMap<String, Cache> allCache = new ConcurrentHashMap<>();
    /**
     * 所有缓存时间的存放处
     */
    private final static ConcurrentMap<String, Long> allCacheTime = new ConcurrentHashMap<>();
    /**
     * 加锁对象
     */
    private final static Object lockObj = new Object();
    /**
     * 3小时缓存
     */
    public final static long cacheTime = 60 * 60 * 3;

    /**
     * 设置缓存（key与value不能为null）,相同类型key value的缓存时间以最小的为准
     *
     * @param key
     * @param value
     * @param expireAfterWrite
     * @param <K>
     * @param <V>
     */
    public static <K, V> void set(K key, V value, long expireAfterWrite) {
        try {
            requireNonNull(key);
            requireNonNull(value);
            String cacheKey = String.join(SPLITSTRING, key.getClass().getTypeName(), value.getClass().getTypeName());
            Cache cache = allCache.get(cacheKey);
            Long cacheTime = allCacheTime.get(key);
            if (cache == null || cacheTime == null || expireAfterWrite < cacheTime) {
                synchronized (lockObj) {
                    cache = allCache.get(cacheKey);
                    cacheTime = allCacheTime.get(key);
                    if (cache == null || cacheTime == null || expireAfterWrite > cacheTime) {
                        Cache<K, V> newCache = Caffeine.newBuilder()
                                .expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
                                .build();
                        allCache.put(cacheKey, newCache);
                        allCacheTime.put(cacheKey, expireAfterWrite);
                        cache = newCache;
                    }
                }
            }
            cache.put(key, value);
        } catch (Exception e) {
            log.error("设置缓存set报错{}", e);
        }
    }

    /**
     * 设置缓存（key与value不能为null）,默认缓存3小时
     *
     * @param key
     * @param value
     * @param <K>
     * @param <V>
     */
    public static <K, V> void set(K key, V value) {
        set(key, value, cacheTime);
    }

    /**
     * 获取缓存（key不能为null）
     *
     * @param key
     * @param typeValue
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> V get(K key, Class<V> typeValue) {
        try {
            requireNonNull(key);
            String cacheKey = String.join(SPLITSTRING, key.getClass().getTypeName(), typeValue.getTypeName());
            Cache<K, V> cache = allCache.get(cacheKey);
            if (cache == null) {
                return null;
            }
            return cache.getIfPresent(key);
        } catch (Exception e) {
            log.error("获取缓存set报错{}", e);
            return null;
        }
    }

    /**
     * 获取缓存，默认Value为Object（key不能为null）
     *
     * @param key
     * @param <K>
     * @return
     */
    public static <K> Object getObject(K key) {
        try {
            return get(key, Object.class);
        } catch (Exception e) {
            log.error("获取缓存set报错{}", e);
            return null;
        }
    }

    /**
     * 获取缓存，默认Value为String（key不能为null）
     *
     * @param key
     * @param <K>
     * @return
     */
    public static <K> String getString(K key) {
        try {
            return get(key, String.class);
        } catch (Exception e) {
            log.error("获取缓存set报错{}", e);
            return null;
        }
    }
}
